package com.area42games.grammar.trees;

import java.util.ArrayList;

import org.antlr.runtime.Token;
import org.antlr.runtime.tree.CommonTree;

import com.area42games.grammar.DiceLexer;

public class DiceTree extends CommonTree 
{	
	protected EvalContext context;
	protected int value = 0;
	protected boolean computed = false;

	protected ArrayList<String> explanations = new ArrayList<String>();

	public DiceTree(Token payload, EvalContext context)
	{
		super(payload);
		this.context = context;
	}

	public DiceTree getDChild(int pos)
	{
		return (DiceTree)getChild(pos);
	}

	public EvalContext getContext()
	{
		return context;
	}

	public int eval(boolean redo) throws InvalidReferenceException
	{
		if(redo)
		{
			computed = false;
			explanations = new ArrayList<String>();
		}
		
		if(computed)
		{
			return value;
		}

		switch(token.getType())
		{
		case DiceLexer.NUMBER :
			value = Integer.parseInt(token.getText());
			break;
		case DiceLexer.PLUS :
			if(getChildren().size() == 1)
			{
				value = getDChild(0).eval(redo);
			}
			else
			{
				value = getDChild(0).eval(redo) + getDChild(1).eval(redo);
			}
			break;
		case DiceLexer.MINUS :
			if(getChildren().size() == 1)
			{
				value = 0-getDChild(0).eval(redo);
			}
			else
			{
				value = getDChild(0).eval(redo) - getDChild(1).eval(redo);
			}
			break;
		case DiceLexer.MULT :
			value = getDChild(0).eval(redo) * getDChild(1).eval(redo);
			break;
		case DiceLexer.DIV :
			value = getDChild(0).eval(redo) / getDChild(1).eval(redo);
			break;
		case DiceLexer.EACH :
			// Need to have a better explain plan
			int numLoop = getDChild(0).eval(redo);
			if(numLoop < 0)
			{
				numLoop = 0;
			}
			int eachResult = 0;

			explanations.add(toString());

			for(int i = 0; i < numLoop; i++)
			{			
				int intermediateValue = getDChild(1).eval(true);
				explanations.add(getDChild(1).explain() + " = " + intermediateValue);
				eachResult += intermediateValue;
			}
			value = eachResult;
			//			explanations.add("total = "+value);
			break;
		case DiceLexer.COMP :
			int left = getDChild(0).eval(redo);
			int right = getDChild(1).eval(redo);
			String comp = token.getText();
			boolean test = false;

			if(comp.equals("<="))
			{
				test = left <= right;
			}
			else if(comp.equals("<"))
			{
				test = left < right;
			}
			else if(comp.equals(">"))
			{
				test = left > right;
			}
			else if(comp.equals(">="))
			{
				test = left >= right;
			}
			else if(comp.equals("=="))
			{
				test = left == right;
			}
			value = test?1:0;
			break;
		case DiceLexer.EQ :
			String var = getDChild(0).getText();
			value = getDChild(1).eval(redo);
			context.set(var,""+value);
			break;
		case DiceLexer.IDENTIFIER :
			String var2 = token.getText();
			String varTree = context.get(var2);
			if(varTree != null)
			{
				int varResult = Integer.parseInt(varTree);
				explanations.add("["+varResult+"]");
				value = varResult;
			}
			else
			{
				throw new InvalidReferenceException(var2);
			}
			break;
		case DiceLexer.QUESTION :
			int tempValue = getDChild(0).eval(redo);		
			context.push(tempValue);
			int choice = getDChild(1).eval(true);
			context.pop();
			value = choice;
			break;
		case DiceLexer.LBRACE :
			int lhs = context.peek();

			int pos = 0;
			for(Object o : getChildren())
			{
				if(o instanceof DiceTree)
				{
					DiceTree option = (DiceTree)o;

					if(option.getType() == DiceLexer.HASH)
					{
						DiceTree range = option.getDChild(0);
						int inRange = 0;
						if(range.getType() == DiceLexer.NUMBER)
						{
							if(lhs == range.eval(true))
							{
								inRange = 1;
							}
						}
						else if(range.getType() == DiceLexer.DOT)
						{
							context.push(lhs);
							inRange = range.eval(true);
							context.pop();
						}

						if(inRange == 1)
						{
							value = option.getDChild(1).eval(true);
							computed = true;
						}
					}
					else
					{
						if(lhs-1 == pos)
						{
							value = option.eval(true);
							computed = true;
						}
					}
				}
				pos++;
				if(computed)
				{
					break;
				}
			}
			if(!computed)
			{
				value = lhs;
				break;
			}
			
			break;
		case DiceLexer.COLON :
			int bool = context.peek();
			if(bool == 1)
			{
				value = getDChild(0).eval(redo);
			}
			else
			{
				value = getDChild(1).eval(redo);
			}
			break;
		case DiceLexer.DOT :
			int within = context.peek();
			if(within >= getDChild(0).eval(redo) && within <= getDChild(1).eval(redo))
			{
				value = 1;
			}
			else
			{
				value = 0;
			}
			break;
		case DiceLexer.LPAREN :
			value = getDChild(0).eval(redo);
			break;
		case DiceLexer.QUALIFIER :
			break;
		}

		computed = true;
		return value;
	}

	public String toString()
	{
		String retval = "";
		String tokenText = "";
		if(token != null)
		{
			if(token.getText().equals("HASH"))
			{
				tokenText = ":";
			}
			else
			{
				tokenText = token.getText();
			}
		}
		else
		{
			tokenText = "root";
		}

		if(children != null && children.size() == 2)
		{
			DiceTree child0 = getDChild(0);
			retval += child0.toString();

			retval += tokenText;

			DiceTree child1 = getDChild(1);
			retval += child1.toString();
		}
		else
		{
			retval += tokenText;

			if(children != null)
			{
				for(Object c : children)
				{
					if(c instanceof DiceTree)
					{
						if(token == null)
						{
							retval += "\n\t";
						}
						retval += c.toString();
						if(tokenText.equals("{"))
						{
							retval+=",";
						}
					}
				}
				if(tokenText.equals("{"))
				{
					retval = retval.substring(0,retval.length()-1);
				}
			}		
		}

		if(tokenText.equals("("))
		{
			retval += ")";
		}
		if(tokenText.equals("{"))
		{
			retval += "}";
		}
		if(tokenText.startsWith("$"))
		{
			retval += " ";
		}

		return retval;
	}

	public String explain()
	{
		String retval = "";

		if(explanations.size() > 0)
		{
			for(String s : explanations)
			{
				retval += s + "\n";
			}
			retval = retval.substring(0,retval.length()-1);
		}
		else
		{
			String tokenText = "";
			if(token != null)
			{
				if(token.getText().equals("HASH"))
				{
					tokenText = ":";
				}
				else
				{
					tokenText = token.getText();
				}
			}
			else
			{
				tokenText = "root";
			}

			if(children != null && children.size() == 2)
			{
				DiceTree child0 = getDChild(0);
				retval += child0.explain();

				retval += tokenText;

				DiceTree child1 = getDChild(1);
				retval += child1.explain();
			}
			else
			{
				retval += tokenText;

				if(children != null)
				{
					for(Object c : children)
					{
						if(c instanceof DiceTree)
						{
							DiceTree cdt = (DiceTree)c;
							if(token == null)
							{
								retval += "\n\t";
							}
							retval += cdt.explain();
							if(tokenText.equals("{"))
							{
								retval+=",";
							}
						}
					}
					if(tokenText.equals("{"))
					{
						retval = retval.substring(0,retval.length()-1);
					}
				}		
			}

			if(tokenText.equals("("))
			{
				retval += ")";
			}
			if(tokenText.equals("{"))
			{
				retval += "}";
			}
			if(tokenText.startsWith("$"))
			{
				retval += " ";
			}
		}				
		return retval;
	}

}
